/*
===========================================================================

Wolfenstein: Enemy Territory GPL Source Code
Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. 

This file is part of the Wolfenstein: Enemy Territory GPL Source Code (Wolf ET Source Code).  

Wolf ET Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Wolf ET Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Wolf ET Source Code.  If not, see <http://www.gnu.org/licenses/>.

In addition, the Wolf: ET Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Wolf ET Source Code.  If not, please request a copy in writing from id Software at the address below.

If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.

===========================================================================
*/

//===========================================================================
//
// Name:				aas_create.c
// Function:		Creation of AAS
// Programmer:		Mr Elusive (MrElusive@demigod.demon.nl)
// Last update:	1997-12-04
// Tab Size:		3
//===========================================================================

#include "qbsp.h"
#include "..\botlib\aasfile.h"
#include "aas_create.h"
#include "aas_store.h"
#include "aas_gsubdiv.h"
#include "aas_facemerging.h"
#include "aas_areamerging.h"
#include "aas_edgemelting.h"
#include "aas_prunenodes.h"
#include "aas_cfg.h"
#include "..\game\surfaceflags.h"

//#define AW_DEBUG
//#define L_DEBUG

#define AREAONFACESIDE( face, area )      ( face->frontarea != area )

tmp_aas_t tmpaasworld;

extern int mingroundarea;           // areas with ground surface area less than this will be removed

//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void AAS_InitTmpAAS( void ) {
	//tmp faces
	tmpaasworld.numfaces = 0;
	tmpaasworld.facenum = 0;
	tmpaasworld.faces = NULL;
	//tmp convex areas
	tmpaasworld.numareas = 0;
	tmpaasworld.areanum = 0;
	tmpaasworld.areas = NULL;
	//tmp nodes
	tmpaasworld.numnodes = 0;
	tmpaasworld.nodes = NULL;
	//
	tmpaasworld.nodebuffer = NULL;
} //end of the function AAS_InitTmpAAS
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void AAS_FreeTmpAAS( void ) {
	tmp_face_t *f, *nextf;
	tmp_area_t *a, *nexta;
	tmp_nodebuf_t *nb, *nextnb;

	//free all the faces
	for ( f = tmpaasworld.faces; f; f = nextf )
	{
		nextf = f->l_next;
		if ( f->winding ) {
			FreeWinding( f->winding );
		}
		FreeMemory( f );
	} //end if
	  //free all tmp areas
	for ( a = tmpaasworld.areas; a; a = nexta )
	{
		nexta = a->l_next;
		if ( a->settings ) {
			FreeMemory( a->settings );
		}
		FreeMemory( a );
	} //end for
	  //free all the tmp nodes
	for ( nb = tmpaasworld.nodebuffer; nb; nb = nextnb )
	{
		nextnb = nb->next;
		FreeMemory( nb );
	} //end for
} //end of the function AAS_FreeTmpAAS
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
tmp_face_t *AAS_AllocTmpFace( void ) {
	tmp_face_t *tmpface;

	tmpface = (tmp_face_t *) GetClearedMemory( sizeof( tmp_face_t ) );
	tmpface->num = tmpaasworld.facenum++;
	tmpface->l_prev = NULL;
	tmpface->l_next = tmpaasworld.faces;
	if ( tmpaasworld.faces ) {
		tmpaasworld.faces->l_prev = tmpface;
	}
	tmpaasworld.faces = tmpface;
	tmpaasworld.numfaces++;
	return tmpface;
} //end of the function AAS_AllocTmpFace
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void AAS_FreeTmpFace( tmp_face_t *tmpface ) {
	if ( tmpface->l_next ) {
		tmpface->l_next->l_prev = tmpface->l_prev;
	}
	if ( tmpface->l_prev ) {
		tmpface->l_prev->l_next = tmpface->l_next;
	} else { tmpaasworld.faces = tmpface->l_next;}
	//free the winding
	if ( tmpface->winding ) {
		FreeWinding( tmpface->winding );
	}
	//free the face
	FreeMemory( tmpface );
	tmpaasworld.numfaces--;
} //end of the function AAS_FreeTmpFace
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
tmp_area_t *AAS_AllocTmpArea( void ) {
	tmp_area_t *tmparea;

	tmparea = (tmp_area_t *) GetClearedMemory( sizeof( tmp_area_t ) );
	tmparea->areanum = tmpaasworld.areanum++;
	tmparea->l_prev = NULL;
	tmparea->l_next = tmpaasworld.areas;
	if ( tmpaasworld.areas ) {
		tmpaasworld.areas->l_prev = tmparea;
	}
	tmpaasworld.areas = tmparea;
	tmpaasworld.numareas++;
	return tmparea;
} //end of the function AAS_AllocTmpArea
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void AAS_FreeTmpArea( tmp_area_t *tmparea ) {
	if ( tmparea->l_next ) {
		tmparea->l_next->l_prev = tmparea->l_prev;
	}
	if ( tmparea->l_prev ) {
		tmparea->l_prev->l_next = tmparea->l_next;
	} else { tmpaasworld.areas = tmparea->l_next;}
	if ( tmparea->settings ) {
		FreeMemory( tmparea->settings );
	}
	FreeMemory( tmparea );
	tmpaasworld.numareas--;
} //end of the function AAS_FreeTmpArea
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
tmp_node_t *AAS_AllocTmpNode( void ) {
	tmp_nodebuf_t *nodebuf;

	if ( !tmpaasworld.nodebuffer ||
		 tmpaasworld.nodebuffer->numnodes >= NODEBUF_SIZE ) {
		nodebuf = (tmp_nodebuf_t *) GetClearedMemory( sizeof( tmp_nodebuf_t ) );
		nodebuf->next = tmpaasworld.nodebuffer;
		nodebuf->numnodes = 0;
		tmpaasworld.nodebuffer = nodebuf;
	} //end if
	tmpaasworld.numnodes++;
	return &tmpaasworld.nodebuffer->nodes[tmpaasworld.nodebuffer->numnodes++];
} //end of the function AAS_AllocTmpNode
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void AAS_FreeTmpNode( tmp_node_t *tmpnode ) {
	tmpaasworld.numnodes--;
} //end of the function AAS_FreeTmpNode
//===========================================================================
// returns true if the face is a gap from the given side
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
int AAS_GapFace( tmp_face_t *tmpface, int side ) {
	vec3_t invgravity;

	//if the face is a solid or ground face it can't be a gap
	if ( tmpface->faceflags & ( FACE_GROUND | FACE_SOLID ) ) {
		return 0;
	}

	VectorCopy( cfg.phys_gravitydirection, invgravity );
	VectorInverse( invgravity );

	return ( DotProduct( invgravity, mapplanes[tmpface->planenum ^ side].normal ) > cfg.phys_maxsteepness );
} //end of the function AAS_GapFace
//===========================================================================
// returns true if the face is a ground face
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
int AAS_GroundFace( tmp_face_t *tmpface ) {
	vec3_t invgravity;

	//must be a solid face
	if ( !( tmpface->faceflags & FACE_SOLID ) ) {
		return 0;
	}

	VectorCopy( cfg.phys_gravitydirection, invgravity );
	VectorInverse( invgravity );

	return ( DotProduct( invgravity, mapplanes[tmpface->planenum].normal ) > cfg.phys_maxsteepness );
} //end of the function AAS_GroundFace
//===========================================================================
// adds the side of a face to an area
//
// side :	0 = front side
//				1 = back side
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void AAS_AddFaceSideToArea( tmp_face_t *tmpface, int side, tmp_area_t *tmparea ) {
	int tmpfaceside;

	if ( side ) {
		if ( tmpface->backarea ) {
			Error( "AAS_AddFaceSideToArea: already a back area\n" );
		}
	} //end if
	else
	{
		if ( tmpface->frontarea ) {
			Error( "AAS_AddFaceSideToArea: already a front area\n" );
		}
	} //end else

	if ( side ) {
		tmpface->backarea = tmparea;
	} else { tmpface->frontarea = tmparea;}

	if ( tmparea->tmpfaces ) {
		tmpfaceside = tmparea->tmpfaces->frontarea != tmparea;
		tmparea->tmpfaces->prev[tmpfaceside] = tmpface;
	} //end if
	tmpface->next[side] = tmparea->tmpfaces;
	tmpface->prev[side] = NULL;
	tmparea->tmpfaces = tmpface;
} //end of the function AAS_AddFaceSideToArea
//===========================================================================
// remove (a side of) a face from an area
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void AAS_RemoveFaceFromArea( tmp_face_t *tmpface, tmp_area_t *tmparea ) {
	int side, prevside, nextside;

	if ( tmpface->frontarea != tmparea &&
		 tmpface->backarea != tmparea ) {
		Error( "AAS_RemoveFaceFromArea: face not part of the area" );
	} //end if
	side = tmpface->frontarea != tmparea;
	if ( tmpface->prev[side] ) {
		prevside = tmpface->prev[side]->frontarea != tmparea;
		tmpface->prev[side]->next[prevside] = tmpface->next[side];
	} //end if
	else
	{
		tmparea->tmpfaces = tmpface->next[side];
	} //end else
	if ( tmpface->next[side] ) {
		nextside = tmpface->next[side]->frontarea != tmparea;
		tmpface->next[side]->prev[nextside] = tmpface->prev[side];
	} //end if
	  //remove the area number from the face depending on the side
	if ( side ) {
		tmpface->backarea = NULL;
	} else { tmpface->frontarea = NULL;}
	tmpface->prev[side] = NULL;
	tmpface->next[side] = NULL;
} //end of the function AAS_RemoveFaceFromArea
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void AAS_CheckArea( tmp_area_t *tmparea ) {
	int side;
	tmp_face_t *face;
	plane_t *plane;
	vec3_t wcenter, acenter = {0, 0, 0};
	vec3_t normal;
	float n, dist;

	if ( tmparea->invalid ) {
		Log_Print( "AAS_CheckArea: invalid area\n" );
	}
	for ( n = 0, face = tmparea->tmpfaces; face; face = face->next[side] )
	{
		//side of the face the area is on
		side = face->frontarea != tmparea;
		WindingCenter( face->winding, wcenter );
		VectorAdd( acenter, wcenter, acenter );
		n++;
	} //end for
	n = 1 / n;
	VectorScale( acenter, n, acenter );
	for ( face = tmparea->tmpfaces; face; face = face->next[side] )
	{
		//side of the face the area is on
		side = face->frontarea != tmparea;

#ifdef L_DEBUG
		if ( WindingError( face->winding ) ) {
			Log_Write( "AAS_CheckArea: area %d face %d: %s\r\n", tmparea->areanum,
					   face->num, WindingErrorString() );
		} //end if
#endif L_DEBUG

		plane = &mapplanes[face->planenum ^ side];

		if ( DotProduct( plane->normal, acenter ) - plane->dist < 0 ) {
			Log_Print( "AAS_CheckArea: area %d face %d is flipped\n", tmparea->areanum, face->num );
			Log_Print( "AAS_CheckArea: area %d center is %f %f %f\n", tmparea->areanum, acenter[0], acenter[1], acenter[2] );
		} //end if
		  //check if the winding plane is the same as the face plane
		WindingPlane( face->winding, normal, &dist );
		plane = &mapplanes[face->planenum];
#ifdef L_DEBUG
		if ( fabs( dist - plane->dist ) > 0.4 ||
			 fabs( normal[0] - plane->normal[0] ) > 0.0001 ||
			 fabs( normal[1] - plane->normal[1] ) > 0.0001 ||
			 fabs( normal[2] - plane->normal[2] ) > 0.0001 ) {
			Log_Write( "AAS_CheckArea: area %d face %d winding plane unequal to face plane\r\n",
					   tmparea->areanum, face->num );
		} //end if
#endif L_DEBUG
	} //end for
} //end of the function AAS_CheckArea
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void AAS_CheckFaceWindingPlane( tmp_face_t *face ) {
	float dist, sign1, sign2;
	vec3_t normal;
	plane_t *plane;
	winding_t *w;

	//check if the winding plane is the same as the face plane
	WindingPlane( face->winding, normal, &dist );
	plane = &mapplanes[face->planenum];
	//
	sign1 = DotProduct( plane->normal, normal );
	//
	if ( fabs( dist - plane->dist ) > 0.4 ||
		 fabs( normal[0] - plane->normal[0] ) > 0.0001 ||
		 fabs( normal[1] - plane->normal[1] ) > 0.0001 ||
		 fabs( normal[2] - plane->normal[2] ) > 0.0001 ) {
		VectorInverse( normal );
		dist = -dist;
		if ( fabs( dist - plane->dist ) > 0.4 ||
			 fabs( normal[0] - plane->normal[0] ) > 0.0001 ||
			 fabs( normal[1] - plane->normal[1] ) > 0.0001 ||
			 fabs( normal[2] - plane->normal[2] ) > 0.0001 ) {
			Log_Write( "AAS_CheckFaceWindingPlane: face %d winding plane unequal to face plane\r\n",
					   face->num );
			//
			sign2 = DotProduct( plane->normal, normal );
			if ( ( sign1 < 0 && sign2 > 0 ) ||
				 ( sign1 > 0 && sign2 < 0 ) ) {
				Log_Write( "AAS_CheckFaceWindingPlane: face %d winding reversed\r\n",
						   face->num );
				w = face->winding;
				face->winding = ReverseWinding( w );
				FreeWinding( w );
			} //end if
		} //end if
		else
		{
			Log_Write( "AAS_CheckFaceWindingPlane: face %d winding reversed\r\n",
					   face->num );
			w = face->winding;
			face->winding = ReverseWinding( w );
			FreeWinding( w );
		} //end else
	} //end if
} //end of the function AAS_CheckFaceWindingPlane
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void AAS_CheckAreaWindingPlanes( void ) {
	int side;
	tmp_area_t *tmparea;
	tmp_face_t *face;

	Log_Write( "AAS_CheckAreaWindingPlanes:\r\n" );
	for ( tmparea = tmpaasworld.areas; tmparea; tmparea = tmparea->l_next )
	{
		if ( tmparea->invalid ) {
			continue;
		}
		for ( face = tmparea->tmpfaces; face; face = face->next[side] )
		{
			side = face->frontarea != tmparea;
			AAS_CheckFaceWindingPlane( face );
		} //end for
	} //end for
} //end of the function AAS_CheckAreaWindingPlanes
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void AAS_FlipAreaFaces( tmp_area_t *tmparea ) {
	int side;
	tmp_face_t *face;
	plane_t *plane;
	vec3_t wcenter, acenter = {0, 0, 0};
	//winding_t *w;
	float n;

	for ( n = 0, face = tmparea->tmpfaces; face; face = face->next[side] )
	{
		if ( !face->frontarea ) {
			Error( "face %d has no front area\n", face->num );
		}
		//side of the face the area is on
		side = face->frontarea != tmparea;
		WindingCenter( face->winding, wcenter );
		VectorAdd( acenter, wcenter, acenter );
		n++;
	} //end for
	n = 1 / n;
	VectorScale( acenter, n, acenter );
	for ( face = tmparea->tmpfaces; face; face = face->next[side] )
	{
		//side of the face the area is on
		side = face->frontarea != tmparea;

		plane = &mapplanes[face->planenum ^ side];

		if ( DotProduct( plane->normal, acenter ) - plane->dist < 0 ) {
			Log_Print( "area %d face %d flipped: front area %d, back area %d\n", tmparea->areanum, face->num,
					   face->frontarea ? face->frontarea->areanum : 0,
					   face->backarea ? face->backarea->areanum : 0 );
			/*
			face->planenum = face->planenum ^ 1;
			w = face->winding;
			face->winding = ReverseWinding(w);
			FreeWinding(w);
			*/
		} //end if
#ifdef L_DEBUG
		{
			float dist;
			vec3_t normal;

			//check if the winding plane is the same as the face plane
			WindingPlane( face->winding, normal, &dist );
			plane = &mapplanes[face->planenum];
			if ( fabs( dist - plane->dist ) > 0.4 ||
				 fabs( normal[0] - plane->normal[0] ) > 0.0001 ||
				 fabs( normal[1] - plane->normal[1] ) > 0.0001 ||
				 fabs( normal[2] - plane->normal[2] ) > 0.0001 ) {
				Log_Write( "area %d face %d winding plane unequal to face plane\r\n",
						   tmparea->areanum, face->num );
			} //end if
		}
#endif
	} //end for
} //end of the function AAS_FlipAreaFaces
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void AAS_RemoveAreaFaceColinearPoints( void ) {
	int side;
	tmp_face_t *face;
	tmp_area_t *tmparea;

	//FIXME: loop over the faces instead of area->faces
	for ( tmparea = tmpaasworld.areas; tmparea; tmparea = tmparea->l_next )
	{
		for ( face = tmparea->tmpfaces; face; face = face->next[side] )
		{
			side = face->frontarea != tmparea;
			RemoveColinearPoints( face->winding );
//			RemoveEqualPoints(face->winding, 0.1);
		} //end for
	} //end for
} //end of the function AAS_RemoveAreaFaceColinearPoints
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void AAS_RemoveTinyFaces( void ) {
	int side, num;
	tmp_face_t *face, *nextface;
	tmp_area_t *tmparea;

	//FIXME: loop over the faces instead of area->faces
	Log_Write( "AAS_RemoveTinyFaces\r\n" );
	num = 0;
	for ( tmparea = tmpaasworld.areas; tmparea; tmparea = tmparea->l_next )
	{
		for ( face = tmparea->tmpfaces; face; face = nextface )
		{
			side = face->frontarea != tmparea;
			nextface = face->next[side];
			//
			if ( WindingArea( face->winding ) < 1 ) {
				if ( face->frontarea ) {
					AAS_RemoveFaceFromArea( face, face->frontarea );
				}
				if ( face->backarea ) {
					AAS_RemoveFaceFromArea( face, face->backarea );
				}
				AAS_FreeTmpFace( face );
				//Log_Write("area %d face %d is tiny\r\n", tmparea->areanum, face->num);
				num++;
			} //end if
		} //end for
	} //end for
	Log_Write( "%d tiny faces removed\r\n", num );
} //end of the function AAS_RemoveTinyFaces
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void AAS_CreateAreaSettings( void ) {
	int i, flags, side, numgrounded, numladderareas, numliquidareas;
	tmp_face_t *face;
	tmp_area_t *tmparea;
	int count;

	numgrounded = 0;
	numladderareas = 0;
	numliquidareas = 0;
	Log_Write( "AAS_CreateAreaSettings\r\n" );
	i = 0;
	qprintf( "%6d areas provided with settings", i );
	for ( tmparea = tmpaasworld.areas; tmparea; tmparea = tmparea->l_next )
	{
		//if the area is invalid there no need to create settings for it
		if ( tmparea->invalid ) {
			continue;
		}

		tmparea->settings = (tmp_areasettings_t *) GetClearedMemory( sizeof( tmp_areasettings_t ) );
		tmparea->settings->contents = tmparea->contents;
		tmparea->settings->modelnum = tmparea->modelnum;
		flags = 0;
		count = 0;
		tmparea->settings->groundsteepness = 0.0;
		for ( face = tmparea->tmpfaces; face; face = face->next[side] )
		{
			side = face->frontarea != tmparea;
			flags |= face->faceflags;
			// Ridah, add this face's steepness
			if ( face->faceflags & FACE_GROUND ) {
				tmparea->settings->groundsteepness += ( 1.0 - mapplanes[face->planenum ^ side].normal[2] );
				count++;
			}
		} //end for
		tmparea->settings->groundsteepness /= (float)count;
		if ( tmparea->settings->groundsteepness > 1.0 ) {
			tmparea->settings->groundsteepness = 1.0;
		}
		if ( tmparea->settings->groundsteepness < 0.0 ) {
			tmparea->settings->groundsteepness = 0.0;
		}
		tmparea->settings->areaflags = 0;
		if ( flags & FACE_GROUND ) {
			tmparea->settings->areaflags |= AREA_GROUNDED;
			numgrounded++;
		} //end if
		if ( flags & FACE_LADDER ) {
			tmparea->settings->areaflags |= AREA_LADDER;
			numladderareas++;
		} //end if
		if ( tmparea->contents & ( AREACONTENTS_WATER |
								   AREACONTENTS_SLIME |
								   AREACONTENTS_LAVA ) ) {
			tmparea->settings->areaflags |= AREA_LIQUID;
			numliquidareas++;
		} //end if
		  //presence type of the area
		tmparea->settings->presencetype = tmparea->presencetype;
		//
		qprintf( "\r%6d", ++i );
	} //end for
	qprintf( "\n" );
#ifdef AASINFO
	Log_Print( "%6d grounded areas\n", numgrounded );
	Log_Print( "%6d ladder areas\n", numladderareas );
	Log_Print( "%6d liquid areas\n", numliquidareas );
#endif //AASINFO
} //end of the function AAS_CreateAreaSettings
//===========================================================================
// create a tmp AAS area from a leaf node
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
//portal_t *globalPortalDebug;

float VectorDistance( vec3_t v1, vec3_t v2 );

int numGroundOnlyFreed;
int numTinyFreed;

tmp_area_t *garea;

tmp_node_t *AAS_CreateArea( node_t *node ) {
	int pside;
	int areafaceflags;
	portal_t    *p;
	tmp_face_t *tmpface;
	tmp_area_t *tmparea;
	tmp_node_t *tmpnode;
	vec3_t up = {0, 0, 1};
//	vec3_t mins, maxs;
//	qboolean allowFreeIfSmall = 1;

//	VectorClear( mins );
//	VectorClear( maxs );

	//create an area from this leaf
	tmparea = AAS_AllocTmpArea();
	tmparea->tmpfaces = NULL;
	//clear the area face flags
	areafaceflags = 0;
	//make aas faces from the portals
	for ( p = node->portals; p; p = p->next[pside] )
	{
		pside = ( p->nodes[1] == node );
		//don't create faces from very small portals
//		if (WindingArea(p->winding) < 1) continue;
		//if there's already a face created for this portal
		if ( p->tmpface ) {
			//add the back side of the face to the area
			AAS_AddFaceSideToArea( p->tmpface, 1, tmparea );
		} //end if
		else
		{
			tmpface = AAS_AllocTmpFace();
			//set the face pointer at the portal so we can see from
			//the portal there's a face created for it
			p->tmpface = tmpface;
			//FIXME: test this change
			//tmpface->planenum = (p->planenum & ~1) | pside;
			tmpface->planenum = p->planenum ^ pside;
			if ( pside ) {
				tmpface->winding = ReverseWinding( p->winding );
			} else { tmpface->winding = CopyWinding( p->winding );}
#ifdef L_DEBUG
			//
			AAS_CheckFaceWindingPlane( tmpface );
#endif //L_DEBUG
			//if there's solid at the other side of the portal
			if ( p->nodes[!pside]->contents & ( CONTENTS_SOLID | CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP ) ) {
				tmpface->faceflags |= FACE_SOLID;
			} //end if
			  //else there is no solid at the other side and if there
			  //is a liquid at this side
			else if ( node->contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) {
				tmpface->faceflags |= FACE_LIQUID;
				//if there's no liquid at the other side
				if ( !( p->nodes[!pside]->contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) ) {
					tmpface->faceflags |= FACE_LIQUIDSURFACE;
				} //end if
			} //end else
			  //if there's ladder contents at other side of the portal
			if ( ( p->nodes[pside]->contents & CONTENTS_LADDER ) ||
				 ( p->nodes[!pside]->contents & CONTENTS_LADDER ) ) {

				//NOTE: doesn't have to be solid at the other side because
				// when standing one can use a crouch area (which is not solid)
				// as a ladder
				// imagine a ladder one can walk underthrough,
				// under the ladder against the ladder is a crouch area
				// the (vertical) sides of this crouch area area also used as
				// ladder sides when standing (not crouched)
				tmpface->faceflags |= FACE_LADDER;
			} //end if
			  //if it is possible to stand on the face
			if ( AAS_GroundFace( tmpface ) ) {
				tmpface->faceflags |= FACE_GROUND;
			} //end if
			  //
			areafaceflags |= tmpface->faceflags;
			//no aas face number yet (zero is a dummy in the aasworld faces)
			tmpface->aasfacenum = 0;
			//add the front side of the face to the area
			AAS_AddFaceSideToArea( tmpface, 0, tmparea );
		} //end else
/*
		// RF, add this face to the bounds
		if (p->tmpface->faceflags & FACE_GROUND) {
			WindingBounds( p->winding, mins, maxs );
		}

		// RF, if this face has a solid at the other side, and it is not a GROUND, then we cannot free if its too small
		if (allowFreeIfSmall && (p->tmpface->faceflags & FACE_SOLID) && !(p->tmpface->faceflags & FACE_GROUND)) {
			// make sure it's not a ceiling
			if (!(DotProduct(cfg.phys_gravitydirection, mapplanes[p->tmpface->planenum].normal) > cfg.phys_maxsteepness)) {
				allowFreeIfSmall = 0;
			}
		}
*/
	} //end for
	qprintf( "\r%6d", tmparea->areanum );
	//presence type in the area
	tmparea->presencetype = ~node->expansionbboxes & cfg.allpresencetypes;
	//
	tmparea->contents = 0;
	if ( node->contents & CONTENTS_CLUSTERPORTAL ) {
		tmparea->contents |= AREACONTENTS_CLUSTERPORTAL;
	}
	if ( node->contents & CONTENTS_MOVER ) {
		tmparea->contents |= AREACONTENTS_MOVER;
	}
	if ( node->contents & CONTENTS_TELEPORTER ) {
		tmparea->contents |= AREACONTENTS_TELEPORTER;
	}
	if ( node->contents & CONTENTS_JUMPPAD ) {
		tmparea->contents |= AREACONTENTS_JUMPPAD;
	}
	if ( node->contents & CONTENTS_DONOTENTER ) {
		tmparea->contents |= AREACONTENTS_DONOTENTER;
	}
	if ( node->contents & CONTENTS_DONOTENTER_LARGE ) {
		tmparea->contents |= AREACONTENTS_DONOTENTER_LARGE;
	}
	if ( node->contents & CONTENTS_WATER ) {
		tmparea->contents |= AREACONTENTS_WATER;
	}
	if ( node->contents & CONTENTS_LAVA ) {
		tmparea->contents |= AREACONTENTS_LAVA;
	}
	if ( node->contents & CONTENTS_SLIME ) {
		tmparea->contents |= AREACONTENTS_SLIME;
	}
/*
	// RF, if we are using only ground areas, then ignore areas that arent grounded or attached to ladders, or underwater
	if (groundonly && !(areafaceflags & (FACE_GROUND|FACE_LADDER)) && !(tmparea->contents & (AREACONTENTS_WATER|AREACONTENTS_SLIME|AREACONTENTS_LAVA))) {
		numGroundOnlyFreed++;
		tmparea->invalid = true;
	}
*/
/*
	// RF, if this is a really small area, and it is surrounded by non-solid portals, then nuke it
	if (allowFreeIfSmall && !(areafaceflags & (FACE_LADDER)) && VectorDistance( mins, maxs ) < 32) {
		numTinyFreed++;
		tmparea->invalid = true;
	}
*/
	//store the bsp model that's inside this node
	tmparea->modelnum = node->modelnum;
	//sorta check for flipped area faces (remove??)
	AAS_FlipAreaFaces( tmparea );
	//check if the area is ok (remove??)
	AAS_CheckArea( tmparea );
	//
	tmpnode = AAS_AllocTmpNode();
	tmpnode->planenum = 0;
	tmpnode->children[0] = 0;
	tmpnode->children[1] = 0;
	tmpnode->tmparea = tmparea;
	//
	return tmpnode;
} //end of the function AAS_CreateArea
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
tmp_node_t *AAS_CreateAreas_r( node_t *node ) {
	tmp_node_t *tmpnode;

	//recurse down to leafs
	if ( node->planenum != PLANENUM_LEAF ) {
		//the first tmp node is a dummy
		tmpnode = AAS_AllocTmpNode();
		tmpnode->planenum = node->planenum;
		tmpnode->children[0] = AAS_CreateAreas_r( node->children[0] );
		tmpnode->children[1] = AAS_CreateAreas_r( node->children[1] );
		return tmpnode;
	} //end if
	  //areas won't be created for solid leafs
	if ( node->contents & CONTENTS_SOLID ) {
		//just return zero for a solid leaf (in tmp AAS NULL is a solid leaf)
		return NULL;
	} //end if

	return AAS_CreateArea( node );
} //end of the function AAS_CreateAreas_r
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void AAS_CreateAreas( node_t *node ) {
	Log_Write( "AAS_CreateAreas\r\n" );
	qprintf( "%6d areas created", 0 );

	numGroundOnlyFreed = 0;
	numTinyFreed = 0;

	tmpaasworld.nodes = AAS_CreateAreas_r( node );
	qprintf( "\n" );

//	Log_Print("%6d non-grounded areas freed\n", numGroundOnlyFreed);
//	Log_Write("%6d non-grounded areas freed\n", numGroundOnlyFreed);
//	Log_Print("%6d tiny areas freed\n", numTinyFreed);
//	Log_Write("%6d tiny areas freed\n", numTinyFreed);

	Log_Write( "%6d areas created\r\n", tmpaasworld.numareas );
} //end of the function AAS_CreateAreas
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void AAS_PrintNumGroundFaces( void ) {
	tmp_face_t *tmpface;
	int numgroundfaces = 0;

	for ( tmpface = tmpaasworld.faces; tmpface; tmpface = tmpface->l_next )
	{
		if ( tmpface->faceflags & FACE_GROUND ) {
			numgroundfaces++;
		} //end if
	} //end for
	qprintf( "%6d ground faces\n", numgroundfaces );
} //end of the function AAS_PrintNumGroundFaces
//===========================================================================
// checks the number of shared faces between the given two areas
// since areas are convex they should only have ONE shared face
// however due to crappy face merging there are sometimes several
// shared faces
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void AAS_CheckAreaSharedFaces( tmp_area_t *tmparea1, tmp_area_t *tmparea2 ) {
	int numsharedfaces, side;
	tmp_face_t *face1, *sharedface;

	if ( tmparea1->invalid || tmparea2->invalid ) {
		return;
	}

	sharedface = NULL;
	numsharedfaces = 0;
	for ( face1 = tmparea1->tmpfaces; face1; face1 = face1->next[side] )
	{
		side = face1->frontarea != tmparea1;
		if ( face1->backarea == tmparea2 || face1->frontarea == tmparea2 ) {
			sharedface = face1;
			numsharedfaces++;
		} //end if
	} //end if
	if ( !sharedface ) {
		return;
	}
	//the areas should only have one shared face
	if ( numsharedfaces > 1 ) {
		Log_Write( "---- tmp area %d and %d have %d shared faces\r\n",
				   tmparea1->areanum, tmparea2->areanum, numsharedfaces );
		for ( face1 = tmparea1->tmpfaces; face1; face1 = face1->next[side] )
		{
			side = face1->frontarea != tmparea1;
			if ( face1->backarea == tmparea2 || face1->frontarea == tmparea2 ) {
				Log_Write( "face %d, planenum = %d, face->frontarea = %d face->backarea = %d\r\n",
						   face1->num, face1->planenum, face1->frontarea->areanum, face1->backarea->areanum );
			} //end if
		} //end if
	} //end if
} //end of the function AAS_CheckAreaSharedFaces
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void AAS_CheckSharedFaces( void ) {
	tmp_area_t *tmparea1, *tmparea2;

	for ( tmparea1 = tmpaasworld.areas; tmparea1; tmparea1 = tmparea1->l_next )
	{
		for ( tmparea2 = tmpaasworld.areas; tmparea2; tmparea2 = tmparea2->l_next )
		{
			if ( tmparea1 == tmparea2 ) {
				continue;
			}
			AAS_CheckAreaSharedFaces( tmparea1, tmparea2 );
		} //end for
	} //end for
} //end of the function AAS_CheckSharedFaces
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void AAS_FlipFace( tmp_face_t *face ) {
	tmp_area_t *frontarea, *backarea;
	winding_t *w;

	frontarea = face->frontarea;
	backarea = face->backarea;
	//must have an area at both sides before flipping is allowed
	if ( !frontarea || !backarea ) {
		return;
	}
	//flip the face winding
	w = face->winding;
	face->winding = ReverseWinding( w );
	FreeWinding( w );
	//flip the face plane
	face->planenum ^= 1;
	//flip the face areas
	AAS_RemoveFaceFromArea( face, frontarea );
	AAS_RemoveFaceFromArea( face, backarea );
	AAS_AddFaceSideToArea( face, 1, frontarea );
	AAS_AddFaceSideToArea( face, 0, backarea );
} //end of the function AAS_FlipFace
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
/*
void AAS_FlipAreaSharedFaces(tmp_area_t *tmparea1, tmp_area_t *tmparea2)
{
	int numsharedfaces, side, area1facing, area2facing;
	tmp_face_t *face1, *sharedface;

	if (tmparea1->invalid || tmparea2->invalid) return;

	sharedface = NULL;
	numsharedfaces = 0;
	area1facing = 0;		//number of shared faces facing towards area 1
	area2facing = 0;		//number of shared faces facing towards area 2
	for (face1 = tmparea1->tmpfaces; face1; face1 = face1->next[side])
	{
		side = face1->frontarea != tmparea1;
		if (face1->backarea == tmparea2 || face1->frontarea == tmparea2)
		{
			sharedface = face1;
			numsharedfaces++;
			if (face1->frontarea == tmparea1) area1facing++;
			else area2facing++;
		} //end if
	} //end if
	if (!sharedface) return;
	//if there's only one shared face
	if (numsharedfaces <= 1) return;
	//if all the shared faces are facing to the same area
	if (numsharedfaces == area1facing || numsharedfaces == area2facing) return;
	//
	do
	{
		for (face1 = tmparea1->tmpfaces; face1; face1 = face1->next[side])
		{
			side = face1->frontarea != tmparea1;
			if (face1->backarea == tmparea2 || face1->frontarea == tmparea2)
			{
				if (face1->frontarea != tmparea1)
				{
					AAS_FlipFace(face1);
					break;
				} //end if
			} //end if
		} //end for
	} while(face1);
} //end of the function AAS_FlipAreaSharedFaces
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void AAS_FlipSharedFaces(void)
{
	int i;
	tmp_area_t *tmparea1, *tmparea2;

	i = 0;
	qprintf("%6d areas checked for shared face flipping", i);
	for (tmparea1 = tmpaasworld.areas; tmparea1; tmparea1 = tmparea1->l_next)
	{
		if (tmparea1->invalid) continue;
		for (tmparea2 = tmpaasworld.areas; tmparea2; tmparea2 = tmparea2->l_next)
		{
			if (tmparea2->invalid) continue;
			if (tmparea1 == tmparea2) continue;
			AAS_FlipAreaSharedFaces(tmparea1, tmparea2);
		} //end for
		qprintf("\r%6d", ++i);
	} //end for
	Log_Print("\r%6d areas checked for shared face flipping\n", i);
} //end of the function AAS_FlipSharedFaces
*/
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void AAS_FlipSharedFaces( void ) {
	int i, side1, side2;
	tmp_area_t *tmparea1;
	tmp_face_t *face1, *face2;

	i = 0;
	qprintf( "%6d areas checked for shared face flipping", i );
	for ( tmparea1 = tmpaasworld.areas; tmparea1; tmparea1 = tmparea1->l_next )
	{
		if ( tmparea1->invalid ) {
			continue;
		}
		for ( face1 = tmparea1->tmpfaces; face1; face1 = face1->next[side1] )
		{
			side1 = face1->frontarea != tmparea1;
			if ( !face1->frontarea || !face1->backarea ) {
				continue;
			}
			//
			for ( face2 = face1->next[side1]; face2; face2 = face2->next[side2] )
			{
				side2 = face2->frontarea != tmparea1;
				if ( !face2->frontarea || !face2->backarea ) {
					continue;
				}
				//
				if ( face1->frontarea == face2->backarea &&
					 face1->backarea == face2->frontarea ) {
					AAS_FlipFace( face2 );
				} //end if
				  //recheck side
				side2 = face2->frontarea != tmparea1;
			} //end for
		} //end for
		qprintf( "\r%6d", ++i );
	} //end for
	qprintf( "\n" );
	Log_Print( "%6d areas checked for shared face flipping\r\n", i );
} //end of the function AAS_FlipSharedFaces
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
int AAS_FacesTouching( tmp_face_t *face1, tmp_face_t *face2 ) {
	int i;
	winding_t *w2;
	plane_t *plane1;
	float dot;

#ifdef DEBUG
	if ( !face1->winding ) {
		Error( "face1 %d without winding", face1->num );
	}
	if ( !face2->winding ) {
		Error( "face2 %d without winding", face2->num );
	}
#endif //DEBUG
	w2 = face2->winding;
	plane1 = &mapplanes[face1->planenum];
	for ( i = 0; i < w2->numpoints; i++ )
	{
		//the point must be on the winding plane
		dot = DotProduct( w2->p[i], plane1->normal ) - plane1->dist;
		if ( dot < -CLIP_EPSILON || dot > CLIP_EPSILON ) {
			return false;
		}
		{
			return 1;
		} //end if
	} //end for
	return 0;
} //end of the function AAS_MeltFaceWinding
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void AAS_RemoveTinyAreas( void ) {
	//RF, disabled, can cause problems, should do checks on minsidelength instead, and also this should be a post-clustering
	// process, as in "-reachableonly"
	return;
#if 0
	int side, gside, nummerges;
	tmp_area_t *tmparea;
	tmp_face_t *face, *gface;
	vec_t windingArea;

	if ( !mingroundarea ) {
		return;
	}

	nummerges = 0;
	Log_Write( "AAS_RemoveTinyAreas\r\n" );
	qprintf( "%6d tiny areas removed", 1 );
	//
	for ( tmparea = tmpaasworld.areas; tmparea; tmparea = tmparea->l_next )
	{
		//if the area is invalid
		if ( tmparea->invalid ) {
			continue;
		} //end if
		  //
		  // never remove liquid areas
		if ( tmparea->contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) {
			continue;
		}
		//
		windingArea = 0;
		//
		for ( face = tmparea->tmpfaces; face; face = face->next[side] )
		{
			side = ( face->frontarea != tmparea );
			//
			// never remove ladder areas
			if ( face->faceflags & FACE_LADDER ) {
				break;
			}
			//
			//if this face touches a grounded face, and there is no area on the other side, then dont remove it
			if ( !( face->faceflags & FACE_GROUND ) ) {
				// does this face share an edge with a ground face?
				for ( gface = tmparea->tmpfaces; gface; gface = gface->next[gside] ) {
					gside = ( gface->frontarea != tmparea );
					//
					if ( gface == face ) {
						continue;
					}
					if ( !( gface->faceflags & FACE_GROUND ) ) {
						continue;
					}
					//
					if ( AAS_FacesTouching( face, gface ) ) {
						break;
					}
				}

				// if this face touches a grounded face, and there is no area on the other side, then this area must stay
				if ( gface && ( !face->frontarea || face->frontarea->invalid || !face->backarea || face->backarea->invalid || ( face->faceflags & FACE_SOLID ) ) ) {
					break;
				}
			}
			//
			if ( face->faceflags & FACE_GROUND ) {
				// get the area of this face
				windingArea += WindingArea( face->winding );
			}
		} //end for
		  //
		  // if this area has a windingArea low enough, then remove it
		if ( !face && windingArea && windingArea < mingroundarea ) {
			qprintf( "\r%6d", ++nummerges );
			tmparea->invalid = 1;
		}

	} //end for
	qprintf( "\n" );
	Log_Write( "%6d tiny areas removed\r\n", nummerges );
#endif
} //end of the function AAS_MergeAreas
//===========================================================================
// creates an .AAS file with the given name
// a MAP should be loaded before calling this
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void AAS_Create( char *aasfile ) {
	entity_t    *e;
	tree_t *tree;
	double start_time;

	//for a possible leak file
	strcpy( source, aasfile );
	StripExtension( source );
	//the time started
	start_time = I_FloatTime();
	//set the default number of threads (depends on number of processors)
	ThreadSetDefault();
	//set the global entity number to the world model
	entity_num = 0;
	//the world entity
	e = &entities[entity_num];
	//
	ResetBrushBSP();
	//
	if ( writebrushmap ) {
		char brushMapName[1024];
		strcpy( brushMapName, aasfile );
		StripExtension( brushMapName );
		strcat( brushMapName, "_aas.map" );
		OpenBSPBrushMap( brushMapName, CONTENTS_SOLID | CONTENTS_MOVER );
	}
	//process the whole world
	tree = ProcessWorldBrushes( e->firstbrush, e->firstbrush + e->numbrushes );
	//if the conversion is cancelled
	if ( cancelconversion ) {
		Tree_Free( tree );
		return;
	} //end if
	  //display BSP tree creation time
	Log_Print( "BSP tree created in %5.0f seconds\n", I_FloatTime() - start_time );
	//prune the bsp tree
	Tree_PruneNodes( tree->headnode );
	//if the conversion is cancelled
	if ( cancelconversion ) {
		Tree_Free( tree );
		return;
	} //end if
	  //create the tree portals
	MakeTreePortals( tree );
	//if the conversion is cancelled
	if ( cancelconversion ) {
		Tree_Free( tree );
		return;
	} //end if
	  //Marks all nodes that can be reached by entites
	if ( FloodEntities( tree ) ) {
		//fill out nodes that can't be reached
		FillOutside( tree->headnode );
	} //end if
	else
	{
		LeakFile( tree );
		Error( "**** leaked ****\n" );
		return;
	} //end else
	  //create AAS from the BSP tree
	  //==========================================
	  //initialize tmp aas
	AAS_InitTmpAAS();
	//create the convex areas from the leaves
	AAS_CreateAreas( tree->headnode );
	//free the BSP tree because it isn't used anymore
	if ( freetree ) {
		Tree_Free( tree );
	}
	//try to merge area faces
	AAS_MergeAreaFaces();
	//do gravitational subdivision
	AAS_GravitationalSubdivision();
	//merge faces if possible
	AAS_MergeAreaFaces();
	AAS_RemoveAreaFaceColinearPoints();
	//merge areas if possible
	AAS_MergeAreas();
	//remove tiny areas
	AAS_RemoveTinyAreas();
	//NOTE: prune nodes directly after area merging
	AAS_PruneNodes();
	//flip shared faces so they are all facing to the same area
	AAS_FlipSharedFaces();
	AAS_RemoveAreaFaceColinearPoints();
	//merge faces if possible
	AAS_MergeAreaFaces();
	//merge area faces in the same plane
	AAS_MergeAreaPlaneFaces();
	//do ladder subdivision
	AAS_LadderSubdivision();
	//FIXME: melting is buggy
	AAS_MeltAreaFaceWindings();
	//remove tiny faces
	AAS_RemoveTinyFaces();
	//create area settings
	AAS_CreateAreaSettings();
	//check if the winding plane is equal to the face plane
	//AAS_CheckAreaWindingPlanes();
	//
	//AAS_CheckSharedFaces();
	//==========================================
	//if the conversion is cancelled
	if ( cancelconversion ) {
		Tree_Free( tree );
		AAS_FreeTmpAAS();
		return;
	} //end if
	  //store the created AAS stuff in the AAS file format and write the file
	AAS_StoreFile( aasfile );
	//free the temporary AAS memory
	AAS_FreeTmpAAS();
	//display creation time
	Log_Print( "\nAAS created in %5.0f seconds\n", I_FloatTime() - start_time );
} //end of the function AAS_Create
